package zookeeper.curator.demo;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.framework.recipes.cache.PathChildrenCache.StartMode;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs.*;
import org.apache.zookeeper.data.Stat;
import zookeeper.curator.MyCuratorWatcher;
import zookeeper.curator.MyWatcher;

import java.util.List;

public class _02_create_node {

	public CuratorFramework client = null;
	public static final String zkServerPath = "hadoop000:2181";

	/**
	 * 实例化zk客户端
	 */
	public _02_create_node() {
		/**
		 * curator链接zookeeper的策略:RetryNTimes
		 * n：重试的次数
		 * sleepMsBetweenRetries：每次重试间隔的时间
		 */
		RetryPolicy retryPolicy = new RetryNTimes(3, 5000);
		

		
		client = CuratorFrameworkFactory.builder()
				.connectString(zkServerPath)
				.sessionTimeoutMs(10000).retryPolicy(retryPolicy)
				.namespace("workspace").build();
		client.start();
	}
	
	/**
	 * 
	 * @Description: 关闭zk客户端连接
	 */
	public void closeZKClient() {
		if (client != null) {
			this.client.close();
		}
	}
	
	public static void main(String[] args) throws Exception {
		// 实例化
		_02_create_node cto = new _02_create_node();
		boolean isZkCuratorStarted = cto.client.isStarted();
		System.out.println("当前客户的状态：" + (isZkCuratorStarted ? "连接中" : "已关闭"));
		

		String nodePath = "/super/xiaoxu";

		//createNode(cto, nodePath,"hello");

		//updateDate(cto, nodePath,"no hello",0);

		//deleteNode(cto, nodePath,1);

		//getDate(cto, nodePath);


		// 查询子节点
		getChildren(cto, nodePath);


		// 判断节点是否存在,如果不存在stat则为空
		//checkExists(cto, nodePath);

		/*
			zookeeper watcher事件
				生命周期：触发一次就会被销毁
				事件类型：增删改
		 */
        //addWatchOnce(cto, nodePath);



		// NodeCache: 监听数据节点的变更（增删改），会【持续】触发事件
		//addWatcherMore(cto, nodePath);


		// 为子节点添加watcher
		addChildrenWacher(cto, nodePath);

		Thread.sleep(100000000);
		
		cto.closeZKClient();
		boolean isZkCuratorStarted2 = cto.client.isStarted();
		System.out.println("当前客户的状态：" + (isZkCuratorStarted2 ? "连接中" : "已关闭"));
	}



























	private static void addWatchOnce(_02_create_node cto, String nodePath) throws Exception {
		// watcher 事件  当使用usingWatcher的时候，监听只会触发一次，监听完毕后就销毁
		cto.client.getData().usingWatcher(new MyCuratorWatcher()).forPath(nodePath);
		cto.client.getData().usingWatcher(new MyWatcher()).forPath(nodePath);
	}


	private static void addChildrenWacher(_02_create_node cto, String nodePath) throws Exception {
		// PathChildrenCache: 监听数据节点的增删改，会触发事件
		String childNodePathCache =  nodePath;
		// cacheData: 设置缓存节点的数据状态
		final PathChildrenCache childrenCache = new PathChildrenCache(cto.client, childNodePathCache, true);
		/**
		 * StartMode: 初始化方式
		 * POST_INITIALIZED_EVENT：异步初始化，初始化之后会触发事件
		 * NORMAL：异步初始化
		 * BUILD_INITIAL_CACHE：同步初始化
		 */
		childrenCache.start(StartMode.POST_INITIALIZED_EVENT);

		List<ChildData> childDataList = childrenCache.getCurrentData();
		System.out.println("当前数据节点的子节点数据列表：");
		for (ChildData cd : childDataList) {
			String childData = new String(cd.getData());
			System.out.println(childData);
		}

		childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
		    //todo 一般配置在数据库/缓存/配置文件中
            public final static String ADD_PATH = "/super/xiaoxu/d";

			public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
				if(event.getType().equals(PathChildrenCacheEvent.Type.INITIALIZED)){
					System.out.println("子节点初始化ok...");
				}

				else if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_ADDED)){
					String path = event.getData().getPath();
					if (path.equals(ADD_PATH)) {
						System.out.println("添加子节点:" + event.getData().getPath());
						System.out.println("子节点数据:" + new String(event.getData().getData()));
					} else if (path.equals("/super/xiaoxu/e")) {
						System.out.println("添加不正确...");
					}

				}else if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)){
					System.out.println("删除子节点:" + event.getData().getPath());
				}else if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
					System.out.println("修改子节点路径:" + event.getData().getPath());
					System.out.println("修改子节点数据:" + new String(event.getData().getData()));
				}
			}
		});
	}

	private static void addWatcherMore(_02_create_node cto, String nodePath) throws Exception {

		final NodeCache nodeCache = new NodeCache(cto.client, nodePath);
		// buildInitial : 初始化的时候获取node的值并且缓存
		nodeCache.start(true);

		if (nodeCache.getCurrentData() != null) {
			System.out.println("节点初始化数据为：" + new String(nodeCache.getCurrentData().getData()));
		} else {
			System.out.println("节点初始化数据为空...");
		}
		nodeCache.getListenable().addListener(new NodeCacheListener() {
			public void nodeChanged() throws Exception {
			    //todo 如果不做判断，删除节点回报【空指针异常】
				if (nodeCache.getCurrentData() == null) {
					System.out.println("空");
					return;
				}
				String data = new String(nodeCache.getCurrentData().getData());
				System.out.println("节点路径：" + nodeCache.getCurrentData().getPath() + "数据：" + data);
			}
		});
	}

	private static void checkExists(_02_create_node cto, String nodePath) throws Exception {
		Stat statExist = cto.client.checkExists().forPath(nodePath);
		System.out.println(statExist);
	}

	private static void getChildren(_02_create_node cto, String nodePath) throws Exception {
		List<String> childNodes = cto.client.getChildren()
											.forPath(nodePath);
		System.out.println("开始打印子节点：");
		for (String s : childNodes) {
			System.out.println(s);
		}
	}

	private static void getDate(_02_create_node cto, String nodePath) throws Exception {
		Stat stat = new Stat();
		byte[] data = cto.client.getData().storingStatIn(stat).forPath(nodePath);
		System.out.println("节点" + nodePath + "的数据为: " + new String(data));
		System.out.println("该节点Stat: " + stat.toString());
	}

	private static void deleteNode(_02_create_node cto, String nodePath,int version) throws Exception {
		// 删除节点
		cto.client.delete()
				.guaranteed()                    // 如果删除失败，那么在后端还是继续会删除，直到成功
				.deletingChildrenIfNeeded()    // 如果有子节点，就删除
				.withVersion(version)
				.forPath(nodePath);
	}

	private static void updateDate(_02_create_node cto, String nodePath,String data ,int version) throws Exception {
		// 更新节点数据
		byte[] newData = data.getBytes();
		cto.client.setData().withVersion(version).forPath(nodePath, newData);
	}

	private static void createNode(_02_create_node cto, String nodePath,String data) throws Exception {
		byte[] dataByte = data.getBytes();
		cto.client.create().creatingParentsIfNeeded()
			.withMode(CreateMode.PERSISTENT)
			.withACL(Ids.OPEN_ACL_UNSAFE)
			.forPath(nodePath, dataByte);
	}


	
}
